package edu.cmu.cs214.hw9.server;


import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.io.PrintWriter;
import java.net.Socket;
import java.util.ArrayList;
import java.util.LinkedList;
import java.util.List;

import edu.cmu.cs214.hw9.db.UserData;
import edu.cmu.cs214.hw9.facelook.Post;

import edu.cmu.cs214.hw9.json.JSONArray;
import edu.cmu.cs214.hw9.json.JSONObject;
import edu.cmu.cs214.hw9.json.JSONTokener;

/**
 * A Thread that listens for incoming messages, and reacts to them appropriately
 * Based heavily on 15-214 DistribLab staff code
 *
 */

public class ServerThread extends Thread {
	private Socket mySocket;
	private UserData ud;
	public ServerThread(Socket s, UserData ud) throws Exception {
		if (s == null) {
			throw new NullPointerException();
		}
		mySocket = s;
		mySocket.setSoTimeout(600000);
		this.ud = ud;
	}
	public void run(){
		try{
			//Stream that the thread writes to
			PrintWriter out = new PrintWriter(mySocket.getOutputStream(),
					true);
			//Stream that the thread reads from
			BufferedReader in = new BufferedReader(new InputStreamReader(
					mySocket.getInputStream()));
			
			while(true){//keep accepting messages until END command is given.
				//reads the message from the input stream, then calls the appropriate database handler method
				String msg = in.readLine();
				
				//Message format is "COMMAND parameter" or "COMMAND p1:p2"
				
				JSONObject o;
				if(msg.indexOf("STORE") == 0){//Recognize the STORE command, store new user's name, email, password
					o = new JSONObject(new JSONTokener(msg.substring(6)));
					boolean success = ud.addUser(o.getString("username"), o.getString("email"), o.getString("password"));
					if (success) out.println("successful store");
					else out.println("STORE failed");
					ud.printDB();
				}
				else if(msg.indexOf("EXISTS") == 0) //command to check if user exists
				{
					boolean exists = ud.userExists(msg.substring(7));
					out.println(exists);
				}
				else if(msg.indexOf("PASSWORD") == 0) //command to get user's password
				{
					String pass = ud.getPassword(msg.substring(9));
					out.println(pass);
				}
				else if (msg.indexOf("NAME") == 0) //command to get user's name
				{
					String name = ud.getName(msg.substring(5));
					out.println(name);
				}
				else if (msg.indexOf("GETF") == 0) //command to get user's friends
				{
					ArrayList<String> jsonFriends = ud.getFriends(msg.substring(5), false);
					out.println(new JSONArray(jsonFriends).toString());
				}
				else if (msg.indexOf("GETP") == 0) //command to get user's pending friend requests
				{
					ArrayList<String> jsonPending = ud.getFriends(msg.substring(5), true);
					out.println(new JSONArray(jsonPending).toString());
				}
				else if(msg.indexOf("FRIENDS") == 0){ //command to see if two users are friends
					boolean areFriends = ud.areFriends(msg.substring(8, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1), false);
					out.println(areFriends);
				}
				else if(msg.indexOf("PENDING") == 0){ //command to see if first friend has friend requested second friend
					boolean arePending = ud.areFriends(msg.substring(8, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1), true);
					out.println(arePending);
				}
				else if(msg.indexOf("REQUEST") == 0){ //command to process first friend requesting second friend's friendship
					boolean request = ud.requestFriend(msg.substring(8, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1));
					out.println(request);
				}
				else if(msg.indexOf("ACCEPT") == 0){ //command to process second friend accepting first's friend request
					boolean accept = ud.acceptFriend(msg.substring(7, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1));
					out.println(accept);
				}
				else if (msg.indexOf("UNFRIEND")==0){ //command to process unfriending
					boolean remove = ud.removeFriend(msg.substring(9, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1));
					out.println(remove);
				}
				else if (msg.indexOf("SUBSCRIBED")==0){ //command to see if first user is subscribed to second
					boolean subscribedTo = ud.isSubscribedTo(msg.substring(11, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1));
					out.println(subscribedTo);
				}
				else if (msg.indexOf("SUBTO")==0){ //command to process first user subscribing to second user
					boolean subTo = ud.subscribeTo(msg.substring(6, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1));
					out.println(subTo);
				}
				else if (msg.indexOf("SUBS")==0){ //command to get user's subscriptions
					ArrayList<String> subs = ud.getSubscriptions(msg.substring(5));
					out.println(new JSONArray(subs).toString());
				}
				else if (msg.indexOf("UNSUB")==0){ //command to unsubscirbe user A from user B
					boolean unSub = ud.unSubscribe(msg.substring(6, msg.lastIndexOf(":")),
							msg.substring(msg.lastIndexOf(":")+1));
					out.println(unSub);
				}
				else if(msg.indexOf("POSTN") == 0){ //command to process notification post
					boolean postn = ud.postMessage(msg.substring(6, msg.indexOf(":")),
							msg.substring(msg.indexOf(":")+1), false);
					out.println(postn);
				}
				else if(msg.indexOf("POSTST") == 0){ //command to process status post
					boolean postst = ud.postMessage(msg.substring(7, msg.indexOf(":")),
							msg.substring(msg.indexOf(":")+1), true);
					out.println(postst);
				}
				else if (msg.indexOf("GETNOT")==0){ //command to get one's posted notifications
					LinkedList<String> notifications = ud.getPosts(msg.substring(7), false);
					out.println(new JSONArray(notifications).toString());
				}
				else if (msg.indexOf("GETSTAT")==0){ //command to get one's posted statuses
					LinkedList<String> status = ud.getPosts(msg.substring(8), true);
					out.println(new JSONArray(status).toString());
				}
				else if(msg.indexOf("END") == 0){//Break out of the loop, no longer accept messages
					out.println("closing connection");
					break;
				}
				else{
					out.println("Unrecognized command");
				}
			}
			//only reaches here on "END" command
			//closes the streams, and the socket, ending the thread
			out.close();
			in.close();
			mySocket.close();
		}catch(Exception e){
			e.printStackTrace();
		}
	}

}
